using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Miwik_Heightmap
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {

    #region fields

        // engine and render
        private DARE.CDare m_Dare;
        private DARE.CScene m_scene;
        private DARE.CRender3D m_render;

        // camera
        private DARE.CFirstPersonCamera m_firstCam;
        private Vector3 m_moveCam;

        private DARE.CNode m_nPointer;

        // key state
        private bool m_rightButtonPressed;
        private bool m_leftButtonPressed;
        private bool m_ctrlPressed;

    #endregion

    #region ctor

        public Game1()
        {
            // create the engine
            m_Dare = new DARE.CDare(this);
        }

        protected override void Initialize()
        {
            m_moveCam = Vector3.Zero;
            m_rightButtonPressed = false;
            m_leftButtonPressed = false;
            base.Initialize();
        }

    #endregion

    #region load/unload content

        protected override void LoadContent()
        {
            // general configuration
            m_Dare.Config.General.WindowTitle = "Terrain Demo LAWL !!!!";
            m_Dare.Config.General.ShowMouseCursor = true;
            m_Dare.Config.General.ShowDebugConsole = true;

            // video configuration
            m_Dare.Config.Video.SetResolution(1024, 768);
            m_Dare.Config.Video.FullScreen = false;
            m_Dare.Config.Video.MultiSampling = true;
            m_Dare.Config.Video.UnlockFps = true;

            // start the engine
            m_Dare.RockNRoll();

            // scene creation
            CreateRender();
            CreateCamera();
            CreateScene();
        }

    #endregion

    #region scene creation

        private void CreateRender()
        {
            // simple use, only one scene and one render
            m_scene = new DARE.CScene("scene");
            m_render = new DARE.CRender3D("render", new DARE.COctree(-1500, 1500, -1500, 1500, -1500, 1500));

            // register our renderer to the scene
            m_scene.RegisterRenderer(m_render);
        }

        private void CreateCamera()
        {
            // create our first person camera
            m_firstCam = new DARE.CFirstPersonCamera("firstCam");
            m_firstCam.Translate(new Vector3(0, 25, 0));
            m_firstCam.LookAt(new Vector3(25, 0, -25));

            // register the camera
            m_render.CameraMgr.RegisterCamera(m_firstCam);
        }

        private void CreateScene()
        {
            m_render.EffectMgr.LighteningEnabled = true;

            m_render.EffectMgr.AmbientLightColor = Color.White;
            m_render.EffectMgr.AmbientLightIntensity = 0.1f;

            m_render.EffectMgr.DirectionalLight0Direction = new Vector3(0.6f, -1, 0.8f);
            m_render.EffectMgr.DirectionalLight0DiffuseColor = Color.White;
            m_render.EffectMgr.DirectionalLight0DiffuseIntensity = 1.0f;

            // display a welcome message
            DisplayWelcomeMessage();

            // show options
            m_render.FillMode = FillMode.Solid;
            m_render.ShowModelNode = true;
            m_render.Partitioner.Show = true;
            m_render.Partitioner.ShowItems = true;

            // display a custom floor and sky in the scene
            m_render.SkyMgr.CreateSkybox("Sky", "CubeMaps/Ct/ct");

            // test
            Test();
        }

        private DARE.CNode m_terrainNode;
        private DARE.CNode m_water0Node;
        private DARE.CNode m_water1Node;

        private void Test()
        {
            // POINTER
            m_nPointer = m_render.CreateNode("nPointer", DARE.EType.DYNAMIC);
            m_nPointer.Entity.CreateBasicSphere(0.5f);
            ((DARE.CBasicModel)m_nPointer.Entity.Model).Color = Color.Red;

            // TERRAIN
            PopTerrain1();

            //// WATER
            //m_water0Node = m_render.CreateNode("Water");
            //m_water0Node.Entity.CreateBasicPlane(1000);
            //m_water0Node.Scale(1f);
            //m_water0Node.Entity.Model.IsTransparent = true;
            //m_water0Node.Translate(new Vector3(0, 0, 0), DARE.CNode.ESpace.PARENT);
            //m_water0Node.Entity.Model.Effect.ColorMapTiles = Vector2.One * 15;
            //m_water0Node.Entity.Model.Effect.ColorMap = DARE.CResourceMgr.Load<Texture2D>("Textures/water_low");
            //m_water0Node.Entity.Model.Effect.Alpha = 0.45f;
            //m_water0Node.AABB = new BoundingBox(new Vector3(-500, -2, -500), new Vector3(500, 2, 500));
            //m_water0Node.Entity.Model.Effect.EmissiveLightIntensity = 1f;

            //m_water1Node = m_render.CreateNode("Water2");
            //m_water1Node.Entity.CreateBasicPlane(1000);
            //m_water1Node.Scale(1f);
            //m_water1Node.Entity.Model.IsTransparent = true;
            //m_water1Node.Translate(new Vector3(0, -0.02f, 0), DARE.CNode.ESpace.PARENT);
            //m_water1Node.Entity.Model.Effect.ColorMapTiles = Vector2.One * 15;
            //m_water1Node.Entity.Model.Effect.ColorMap = DARE.CResourceMgr.Load<Texture2D>("Textures/water_low");
            //m_water1Node.Entity.Model.Effect.Alpha = 0.45f;
            //m_water1Node.AABB = new BoundingBox(new Vector3(-500, -2, -500), new Vector3(500, 2, 500));
            //m_water1Node.Entity.Model.Effect.EmissiveLightIntensity = 1f;
        }

        private void PopTerrain0()
        {
            if (m_terrainNode != null)
                m_terrainNode.Remove();

            DARE.CTerrainInfo terrainInfo = new DARE.CTerrainInfo(
                DARE.CResourceMgr.Load<Texture2D>("Heightmaps/ps_height_2k"), 150, 1);
            DARE.CTerrainModel terrainModel = new DARE.CTerrainModel(terrainInfo, 32, 32);

            terrainModel.SetTexture(DARE.CResourceMgr.Load<Texture2D>("Heightmaps/ps_texture_2k"));

            m_terrainNode = terrainModel.RootNode.SetName("nTerrain");
            m_render.AddNode(m_terrainNode);
            m_terrainNode.TranslateY(-5);

            m_terrainNode.Entity.CreatePhysicTerrain(terrainInfo);

            m_terrainNode.Entity.Physic.CollisionType = DARE.APhysicObject.ECollisionType.GROUND;
        }

        private void PopTerrain1()
        {
            if (m_terrainNode != null)
                m_terrainNode.Remove();

            DARE.CTerrainInfo terrainInfo = new DARE.CTerrainInfo(
                DARE.CResourceMgr.Load<Texture2D>("Heightmaps/hTest_256"), 25, 1);
            DARE.CTerrainModel terrainModel = new DARE.CTerrainModel(terrainInfo, 16, 16);

            terrainModel.SetTexture(DARE.CResourceMgr.Load<Texture2D>("Heightmaps/cTest_256"),
                DARE.CResourceMgr.Load<Texture2D>("Textures/grass"), Vector2.One * 15,
                DARE.CResourceMgr.Load<Texture2D>("Textures/rock"), Vector2.One * 15,
                DARE.CResourceMgr.Load<Texture2D>("Textures/moss"), Vector2.One * 15,
                DARE.CResourceMgr.Load<Texture2D>("Textures/gravel"), Vector2.One * 15);

            m_terrainNode = terrainModel.RootNode.SetName("nTerrain");
            m_render.AddNode(m_terrainNode);
            m_terrainNode.TranslateY(-5);

            m_terrainNode.Entity.CreatePhysicTerrain(terrainInfo);

            m_terrainNode.Entity.Physic.CollisionType = DARE.APhysicObject.ECollisionType.GROUND;
        }

    #endregion

    #region run

        protected override void Update(GameTime gameTime)
        {
            ProcessKeyPressed();
            ProcessKeyReleased();
            ProcessMousePressed();
            ProcessMouseReleased();
            ProcessMouseMoved();
            ProcessMouseWheel();
            ProcessRaycast();
            ApplyMovement();
            base.Update(gameTime);
        }

        private void ProcessRaycast()
        {
            foreach (DARE.CRayCast.CIntersectInfo info in m_render.RayCast.IntersectOrderFromCenterToMouse())
            {
                m_nPointer.SetPosition(info.pos, DARE.CNode.ESpace.WORLD);
                break;

                switch (info.node.Entity.Physic.CollisionType)
                {
                    case DARE.APhysicObject.ECollisionType.GROUND:
                        break;
                }
            }
        }

        private void ApplyMovement()
        {
            float fixedTime = m_Dare.Config.Time.FixedTimeSinceLastFrame;
            float fixedMoveSpeed = fixedTime / 200.0f;
            float fixedRotateSpeed = fixedTime / 500.0f;

            if (m_rightButtonPressed)
            {
                if (m_ctrlPressed)
                    fixedMoveSpeed *= 20;
                m_firstCam.Translate(m_moveCam * fixedMoveSpeed * 2);
            }
            if ((m_water0Node != null) && (m_water1Node != null))
            {
                m_water0Node.Entity.Model.Effect.ColorMapOffset += Vector2.UnitX * 0.00001f * DARE.CDare.Instance.Config.Time.TimeSinceLastFrame;
                m_water1Node.Entity.Model.Effect.ColorMapOffset -= Vector2.UnitY * 0.000012f * DARE.CDare.Instance.Config.Time.TimeSinceLastFrame;
            }
        }

        private Texture2D dare = null;

        protected override void Draw(GameTime gameTime)
        {
            base.Draw(gameTime);

            if (dare == null)
                dare = DARE.CResourceMgr.Load<Texture2D>("Logo/DARE_small");
            m_Dare.SpriteBatch.Begin();
            m_Dare.SpriteBatch.Draw(dare, new Rectangle(0, 0, (int)(dare.Width / 1.5f), (int)(dare.Height / 1.5f)), Color.White);
            m_Dare.SpriteBatch.End();
        }

    #endregion

    #region input

        private void PopPhysicBox()
        {
            DARE.CNode box = m_render.CreateNode("nBox_" + m_Dare.IdCounterString, DARE.EType.DYNAMIC);

            box.SetPosition(25, 35, -25, DARE.CNode.ESpace.WORLD);
            box.Entity.CreateBasicBox();
            box.Entity.CreatePhysicBox();
        }

        private void PopPhysicSphere()
        {
            DARE.CNode sphere = m_render.CreateNode("nSphere_" + m_Dare.IdCounterString, DARE.EType.DYNAMIC);

            sphere.SetPosition(25, 35, -25, DARE.CNode.ESpace.WORLD);
            sphere.Entity.CreateBasicSphere();
            sphere.Entity.CreatePhysicSphere();
        }

        private void ProcessKeyPressed()
        {
            foreach (Keys key in m_Dare.InputMgr.Keyboard.KeyPressedEvent)
                switch (key)
                {
                    // exit
                    case Keys.Escape: this.Exit(); break;

                    // video settings
                    case Keys.F1:
                        m_Dare.Config.Video.SetResolution(1024, 768);
                        m_Dare.Config.Video.FullScreen = false;
                        break;
                    case Keys.F2:
                        m_Dare.Config.Video.SetResolution(1680, 1050);
                        m_Dare.Config.Video.FullScreen = true;
                        break;
                    case Keys.F4: m_Dare.Config.Video.MultiSampling = !m_Dare.Config.Video.MultiSampling; break;

                    // show node model
                    case Keys.N: m_render.ShowModelNode = !m_render.ShowModelNode; break;
                    case Keys.D1: m_render.FillMode = FillMode.Solid; break;
                    case Keys.D2: m_render.FillMode = FillMode.WireFrame; break;
                    case Keys.D3: m_render.ShowCollision = !m_render.ShowCollision; break;
                    case Keys.D4: m_render.CollisionFillMode = FillMode.Solid; break;
                    case Keys.D5: m_render.CollisionFillMode = FillMode.WireFrame; break;
                    case Keys.D6: m_render.Partitioner.Show = !m_render.Partitioner.Show; break;
                    case Keys.D7: m_render.Partitioner.ShowItems = !m_render.Partitioner.ShowItems; break;
                    case Keys.D8: m_firstCam.ManualAspectRatio = !m_firstCam.ManualAspectRatio; break;
                    case Keys.D9: m_firstCam.AspectRatio += 0.1f; break;
                    case Keys.D0: m_firstCam.AspectRatio -= 0.1f; break;
                    case Keys.O: m_firstCam.FovY += 0.1f; break;
                    case Keys.P: m_firstCam.FovY -= 0.1f; break;

                    case Keys.Q: PopPhysicBox(); break;
                    case Keys.E: PopPhysicSphere(); break;
                    case Keys.Z:
                        m_water0Node.IsActive = !m_water0Node.IsActive;
                        m_water1Node.IsActive = !m_water1Node.IsActive;
                        break;
                    case Keys.X: m_terrainNode.IsActive = !m_terrainNode.IsActive; break;

                    // terrain
                    case Keys.R: PopTerrain0(); break;
                    case Keys.F: PopTerrain1(); break;

                    // camera
                    case Keys.W: m_moveCam.Z -= 1; break;
                    case Keys.S: m_moveCam.Z += 1; break;
                    case Keys.A: m_moveCam.X -= 1; break;
                    case Keys.D: m_moveCam.X +=1; break;
                    case Keys.LeftShift: m_moveCam.Y -= 1; break;
                    case Keys.Space: m_moveCam.Y += 1; break;
                    case Keys.LeftControl: m_ctrlPressed = !m_ctrlPressed; break;
                    
                    default: break;
                }
        }

        private void ProcessKeyReleased()
        {
            foreach (Keys key in m_Dare.InputMgr.Keyboard.KeyReleasedEvent)
                switch (key)
                {
                    // camera
                    case Keys.W: m_moveCam.Z += 1; break;
                    case Keys.S: m_moveCam.Z -= 1; break;
                    case Keys.A: m_moveCam.X += 1; break;
                    case Keys.D: m_moveCam.X -= 1; break;
                    case Keys.LeftShift: m_moveCam.Y += 1; break;
                    case Keys.Space: m_moveCam.Y -= 1; break;

                    default: break;
                }
        }

        private void ProcessMousePressed()
        {
            foreach (DARE.CMouse.EButton button in m_Dare.InputMgr.Mouse.ButtonPressedEvent)
            {
                switch (button)
                {
                    case DARE.CMouse.EButton.RIGHT: m_rightButtonPressed = true; break;
                    case DARE.CMouse.EButton.LEFT: m_leftButtonPressed = true; break;

                    default: break;
                }
                m_Dare.InputMgr.Mouse.IsCentered = m_rightButtonPressed || m_leftButtonPressed;
                m_Dare.Config.General.ShowMouseCursor = !(m_rightButtonPressed || m_leftButtonPressed);
            }
        }

        private void ProcessMouseReleased()
        {
            foreach (DARE.CMouse.EButton button in m_Dare.InputMgr.Mouse.ButtonReleasedEvent)
            {
                switch (button)
                {
                    case DARE.CMouse.EButton.RIGHT: m_rightButtonPressed = false; break;
                    case DARE.CMouse.EButton.LEFT: m_leftButtonPressed = false; break;

                    default: break;
                }
                m_Dare.InputMgr.Mouse.IsCentered = m_rightButtonPressed || m_leftButtonPressed;
                m_Dare.Config.General.ShowMouseCursor = !(m_rightButtonPressed || m_leftButtonPressed);
            }
        }

        private void ProcessMouseMoved()
        {
            Vector2 mouseMoved = m_Dare.InputMgr.Mouse.MoveEvent.Movement;

            // first cam orientation
            if (m_Dare.InputMgr.Mouse.HasMoved && m_rightButtonPressed)
                m_firstCam.Rotate(new Vector3(-mouseMoved.Y / 400.0f, -mouseMoved.X / 400.0f, 0.0f));
            if (m_Dare.InputMgr.Mouse.HasMoved && m_leftButtonPressed)
            {

            }
        }

        private void ProcessMouseWheel()
        {
        }

    #endregion

    #region display methods

        private void DisplayWelcomeMessage()
        {
            m_Dare.DebugMgr.DebugConsole.WriteLine(
                "===========================================================================\n" + 
                "Welcome to the debug test area !\n" +
                "      TAB - display usage.\n" + 
                "===========================================================================\n");
        }

        private void DisplayUsage()
        {
            m_Dare.DebugMgr.DebugConsole.WriteLine(
                "===========================================================================\n" + 
                "Usage:\n" + 
                "Huuuu??!!!\n" +
                "===========================================================================\n");
        }

    #endregion

    }
}
